package com.mspbots.web.core.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.web.BaseService;
import com.mspbots.web.core.model.TimeLog;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Map;

/**
 * com.mspbots.web.core.service.TimeLogService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/7/2
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class TimeLogService extends BaseService {

    public Mono<JsonNode> selectPage(Map<String, Object> params) {
        return this.coreClient.get("/attendances/page", params);
    }

    public Flux<TimeLog> allUserDay(Map<String, Object> params) {
        Assert.notNull(params.get("tenantId"), () -> "TenantId is required and cannot be empty.");
        return this.coreClient.getFlux("/attendances/all-user", params, TimeLog.class)
                .flatMap(timeLog -> {
                    if (!ObjectUtils.isEmpty(timeLog.getLogTime())) {
                        return this.coreClient.get("/attendances/find-one",
                                Map.of("tenantId", timeLog.getTenantId(), "userId", timeLog.getTenantUserId(),
                                        "startTime", timeLog.getLogTime().minusHours(15).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
                                        "endTime", timeLog.getLogTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
                                        "teamsId", timeLog.getTeamsUserId(), "type", "IN"), TimeLog.class)
                                .switchIfEmpty(Mono.defer(() -> getTardy(timeLog)))
                                .map(log -> {
                                    timeLog.setInTime(log.getLogTime());
                                    return timeLog;
                                });
                    }
                    return getTardy(timeLog);
                });

    }

    private Mono<TimeLog> getTardy(TimeLog timeLog) {
        return this.coreClient.get("/tenant/user-configuration/keyword/work_time", Map.of("tenantId", timeLog.getTenantId(),
                "tenantUserId", timeLog.getTenantUserId()))
                .switchIfEmpty(Mono.defer(() -> {
                    ObjectNode objectNode = this.objectMapper.createObjectNode();
                    try {
                        objectNode.put("keywordValue", this.objectMapper.writeValueAsString(Map.of("workTimes", new ArrayList())));
                    } catch (JsonProcessingException e) {
                        log.error("writeValueAsString workTimes json str is error {}", e.getMessage());
                    }
                    return Mono.just(objectNode);
                }))
                .map(jsonNode -> {
                    try {
                        JsonNode values = this.objectMapper.readValue(jsonNode.get("keywordValue").asText(), JsonNode.class);
                        JsonNode workTimes = values.get("workTimes");
                        workTimes.forEach(node -> {
                            if (node.get("day").asText().equalsIgnoreCase(LocalDateTime.now().getDayOfWeek().name())) {
                                LocalTime start = !StringUtils.isEmpty(node.get("startTime").asText()) ? LocalTime.parse(node.get("startTime").asText())
                                        : LocalTime.of(8, 0);
                                LocalTime end = !StringUtils.isEmpty(node.get("endTime").asText()) ? LocalTime.parse(node.get("endTime").asText())
                                        : LocalTime.of(18, 0);
                                ZoneId zoneId = values.get("offset") != null ? ZoneId.of(values.get("offset").asText()) : ZoneId.systemDefault();
                                LocalTime now = LocalTime.now(zoneId);
                                if (now.isAfter(start) && now.isBefore(end)) {
                                    timeLog.setType("TARDY");
                                }
                            }
                        });
                    } catch (IOException e) {
                        log.error("convert workTimes json str is error {}", jsonNode);
                    }
                    return timeLog;
                });
    }
}
